/*
 * @file main.cpp
 * @author zhangpeihao
 */

#include <gtest/gtest.h>
#include <zsystem/core/common/types.h>
#include <zsystem/core/common/log.h>
#include <zsystem/core/common/event_types.h>
#include <zsystem/chatServer/chatObjects.h>
#include <zsystem/chatServer/chatserver_config.h>
#include "utcase.h"
#include <zsystem/chatServer/chatserver.h>



#define TEST_all                0

#define TEST_room               0
#define TEST_user               0
#define TEST_kxPacketData       0
#define TEST_json               0
#define TEST_md5                1

#define PROP_ID_SYSTEM_STATUS 1001
#define PROP_ID_SYSTEM_NAME 1002
#define PROP_ID_ROOM_NAME 2001
#define PROP_ID_ROOM_ID 2002
#define PROP_ID_ROOM_LOAD 2003
#define PROP_ID_USER_ID 3001
#define PROP_ID_USER_NICKNAME 3002
#define PROP_ID_USER_CONTRIBUTION 3003
#define PROP_ID_USER_INCOME 3004
#define PROP_ID_USER_CONTRIBUTIONLEVEL 3005
#define PROP_ID_USER_INCOMELEVEL 3006


#ifdef __WIN32__
const char* objectScheme = "..\\..\\chatServer\\objects.xml";
const char* propScheme = "..\\..\\chatServer\\props.xml";
#endif
#ifdef __LINUX__
const char* objectScheme = "../../chatServer/objects.xml";
const char* propScheme = "../../chatServer/props.xml";
#endif


#if TEST_all || TEST_room

bool roomSortFunctiong(chatObject::room* left, chatObject::room* right)
{
  if ( left == Z_NULL )
    return false;
  if ( right == Z_NULL )
    return true;
  return ( (*left) < (*right) );
}

TEST_F(utcase, room)
{
  chatObject::room testRoom1;
  testRoom1.lock();
  EXPECT_FALSE(testRoom1.tryLock());

  EXPECT_TRUE(Z_NULL == testRoom1.getProp(PROP_ID_ROOM_NAME));
  EXPECT_TRUE(Z_NULL == testRoom1.getProp(PROP_ID_ROOM_ID));
  EXPECT_TRUE(Z_NULL == testRoom1.getProp(PROP_ID_ROOM_LOAD));

  zsystem::metaProp* room1_name(Z_NULL);
  zsystem::metaProp* room1_id(Z_NULL);
  zsystem::metaProp* room1_load(Z_NULL);
  EXPECT_TRUE(Z_NULL != (room1_name = testRoom1.getProp(PROP_ID_ROOM_NAME, Z_TRUE)));
  EXPECT_TRUE(Z_NULL != (room1_id = testRoom1.getProp(PROP_ID_ROOM_ID, Z_TRUE)));
  EXPECT_TRUE(Z_NULL != (room1_load = testRoom1.getProp(PROP_ID_ROOM_LOAD, Z_TRUE)));

  zsystem::metaProp* temp(Z_NULL);
  EXPECT_TRUE(Z_NULL != (temp = testRoom1.getProp(PROP_ID_ROOM_NAME, Z_TRUE)));
  EXPECT_EQ(room1_name, temp);

  EXPECT_TRUE(Z_NULL != (temp = testRoom1.getProp(PROP_ID_ROOM_NAME)));
  EXPECT_EQ(room1_name, temp);

  EXPECT_TRUE(Z_NULL != (temp = testRoom1.getProp(PROP_ID_ROOM_NAME, Z_FALSE)));
  EXPECT_EQ(room1_name, temp);

  EXPECT_FALSE(room1_name->isList());
  EXPECT_FALSE(room1_name->set(int(100)));
  EXPECT_FALSE(room1_name->set((Z_I64)(1000000)));
  EXPECT_FALSE(room1_name->set((Z_FLOAT)(0.2f)));
  EXPECT_TRUE(room1_name->set("room1"));

  char buffer[1024];
  Z_I32 len;
  EXPECT_TRUE(room1_name->getString(buffer, 1024, len));
  EXPECT_EQ(5, len);
  EXPECT_STREQ("room1", buffer);

  len = 0;
  std::string strTmp("room2");
  EXPECT_TRUE(room1_name->set(strTmp));
  EXPECT_TRUE(room1_name->getString(buffer, 1024, len));
  EXPECT_EQ(5, len);
  EXPECT_STREQ("room2", buffer);

  EXPECT_TRUE(room1_id->set(Z_I32(1)));
  EXPECT_TRUE(room1_load->set(Z_FLOAT(0.01f)));

  testRoom1.unlock();

  chatObject::room testRoom2;
  zsystem::metaProp* room2_name(Z_NULL);
  zsystem::metaProp* room2_id(Z_NULL);
  zsystem::metaProp* room2_load(Z_NULL);
  EXPECT_TRUE(Z_NULL != (room2_name = testRoom2.getProp(PROP_ID_ROOM_NAME, Z_TRUE)));
  EXPECT_TRUE(Z_NULL != (room2_id = testRoom2.getProp(PROP_ID_ROOM_ID, Z_TRUE)));
  EXPECT_TRUE(Z_NULL != (room2_load = testRoom2.getProp(PROP_ID_ROOM_LOAD, Z_TRUE)));

  EXPECT_TRUE(room2_name->set("room2"));
  EXPECT_TRUE(room2_id->set(Z_I32(2)));
  EXPECT_TRUE(room2_load->set(Z_FLOAT(0.02f)));

  EXPECT_TRUE(testRoom1 < testRoom2);
  EXPECT_TRUE(room1_id->set(Z_I32(3)));
  EXPECT_FALSE(testRoom1 < testRoom2);
  EXPECT_TRUE(room1_id->set(Z_I32(2)));
  EXPECT_FALSE(testRoom1 < testRoom2);

  EXPECT_TRUE(room1_id->set(Z_I32(1)));

  chatObject::room testRoom3;
  zsystem::metaProp* room3_name(Z_NULL);
  zsystem::metaProp* room3_id(Z_NULL);
  zsystem::metaProp* room3_load(Z_NULL);
  EXPECT_TRUE(Z_NULL != (room3_name = testRoom3.getProp(PROP_ID_ROOM_NAME, Z_TRUE)));
  EXPECT_TRUE(Z_NULL != (room3_id = testRoom3.getProp(PROP_ID_ROOM_ID, Z_TRUE)));
  EXPECT_TRUE(Z_NULL != (room3_load = testRoom3.getProp(PROP_ID_ROOM_LOAD, Z_TRUE)));

  EXPECT_TRUE(room3_name->set("room3"));
  EXPECT_TRUE(room3_id->set(Z_I32(3)));
  EXPECT_TRUE(room3_load->set(Z_FLOAT(0.03f)));

  zsystem::stdMap<Z_I32, zsystem::mutexLock> roomMap;
  EXPECT_TRUE(roomMap.insert(1, &testRoom1));
  EXPECT_TRUE(roomMap.insert(2, &testRoom2));
  EXPECT_TRUE(roomMap.insert(3, &testRoom3));
  Z_PTR ptrTemp(Z_NULL);
  EXPECT_FALSE(roomMap.lookup(0, ptrTemp));
  EXPECT_TRUE(roomMap.lookup(1, ptrTemp));
  EXPECT_TRUE(&testRoom1 == ptrTemp);
  EXPECT_TRUE(roomMap.lookup(2, ptrTemp));
  EXPECT_TRUE(&testRoom2 == ptrTemp);
  EXPECT_TRUE(roomMap.lookup(3, ptrTemp));
  EXPECT_TRUE(&testRoom3 == ptrTemp);

  std::list<chatObject::room*> roomList;
  roomList.push_back(&testRoom2);
  roomList.push_back(&testRoom1);
  roomList.push_back(&testRoom3);
  roomList.push_back(Z_NULL);
  roomList.sort(roomSortFunctiong);

  int i = 1;
  std::list<chatObject::room*>::iterator it;
  for ( it = roomList.begin(); it != roomList.end(); it++, i++)
  {
    switch(i)
    {
    case 1:
      EXPECT_TRUE(&testRoom1 == *it);
      break;
    case 2:
      EXPECT_TRUE(&testRoom2 == *it);
      break;
    case 3:
      EXPECT_TRUE(&testRoom3 == *it);
      break;
    case 4:
      EXPECT_TRUE(Z_NULL == *it);
      break;
    default:
      GTEST_FAIL();
      break;
    }
  }


}
#endif

#if TEST_all || TEST_user

bool userSortFunctiong(chatObject::user* left, chatObject::user* right)
{
  if ( left == Z_NULL )
    return false;
  if ( right == Z_NULL )
    return true;
  return ( (*left) < (*right) );
}

struct test_user_data{
  Z_I64 income;
  Z_I64 contribution;
};

/*
999999999999LL, 99999999999999LL, 4
999999999999LL, 99999999999999LL, 5
999999999999LL, 99999999999998LL, 2
999999999998LL, 99999999999999LL, 1
999999999998LL, 99999999999998LL, 3
*/
test_user_data testUserData[] = {
  { 999999999998LL, 99999999999999LL},
  { 999999999999LL, 99999999999998LL},
  { 999999999998LL, 99999999999998LL},
  { 999999999999LL, 99999999999999LL},
  { 999999999999LL, 99999999999999LL}
};

Z_I32 testSortId[] = {4,5,2,1,3};


TEST_F(utcase, user)
{
  Z_I32 testNumber = sizeof(testSortId)/ sizeof(Z_I32);
  chatObject::user testUser[sizeof(testSortId)/ sizeof(Z_I32)];

  zsystem::metaProp* user_id(Z_NULL);
  zsystem::metaProp* user_nickname(Z_NULL);
  zsystem::metaProp* user_contribution(Z_NULL);
  zsystem::metaProp* user_income(Z_NULL);
  zsystem::metaProp* user_contributionLevel(Z_NULL);
  zsystem::metaProp* user_incomeLevel(Z_NULL);

#define SET_USER_DATA(ID)                                                                                           \
  EXPECT_TRUE(Z_NULL != (user_id = testUser[ID-1].getProp(PROP_ID_USER_ID, Z_TRUE)));                                  \
  EXPECT_TRUE(Z_NULL != (user_nickname = testUser[ID-1].getProp(PROP_ID_USER_NICKNAME, Z_TRUE)));                      \
  EXPECT_TRUE(Z_NULL != (user_contribution = testUser[ID-1].getProp(PROP_ID_USER_CONTRIBUTION, Z_TRUE)));              \
  EXPECT_TRUE(Z_NULL != (user_income = testUser[ID-1].getProp(PROP_ID_USER_INCOME, Z_TRUE)));                          \
  EXPECT_TRUE(Z_NULL != (user_contributionLevel = testUser[ID-1].getProp(PROP_ID_USER_CONTRIBUTIONLEVEL, Z_TRUE)));    \
  EXPECT_TRUE(Z_NULL != (user_incomeLevel = testUser[ID-1].getProp(PROP_ID_USER_INCOMELEVEL, Z_TRUE)));                \
  EXPECT_TRUE(user_id->set(Z_I32(ID)));                                                                             \
  EXPECT_TRUE(user_nickname->set("user"#ID));                                                                      \
  EXPECT_TRUE(user_contribution->set(Z_I64(testUserData[ID-1].contribution)));                                      \
  EXPECT_TRUE(user_income->set(Z_I64(testUserData[ID-1].income)));                                                  \
  EXPECT_TRUE(user_contributionLevel->set(Z_I32(123)));                                                             \
  EXPECT_TRUE(user_incomeLevel->set(Z_I32(321)));

  SET_USER_DATA(1)
  SET_USER_DATA(2)
  SET_USER_DATA(3)
  SET_USER_DATA(4)
  SET_USER_DATA(5)

  std::list<chatObject::user*> userList;
  for ( int i = 0; i < testNumber; i++ )
  {
    userList.push_back(&testUser[i]);
  }
  userList.sort(userSortFunctiong);

  int i = 0;
  std::list<chatObject::user*>::iterator it;
  for ( it = userList.begin(); it != userList.end() && i < testNumber; it++, i++)
  {
    EXPECT_TRUE(&testUser[testSortId[i] - 1] == *it) << "i = " << i << ", id = " << ( 1 + ( (Z_PTR_TYPE)(*it) - (Z_PTR_TYPE)(&testUser[0]) ) / sizeof(chatObject::user));
  }

  EXPECT_EQ(testNumber, i);
}

#endif


#if TEST_all || TEST_kxPacketData
#include <zsystem/chatServer/kxPacketData.pb.h>
#define TEST_PROTOBUF_BUFFER_SIZE 1024
#define TEST_PROTOBUF_ID          100001
#define TEST_PROTOBUF_ROOMID      123
#define TEST_PROTOBUF_SIGN        "1234567890ABCDEFGHIJK"
#define TEST_PROTOBUF_PLATFORM    11
TEST_F(utcase, kxPacketData)
{
  kxPacketData::LoginRequest loginRequest;
  Z_I32 timestamp = (Z_I32)(time(Z_NULL));
  loginRequest.set_uid(TEST_PROTOBUF_ID);
  loginRequest.set_room_id(TEST_PROTOBUF_ROOMID);
  loginRequest.set_timestamp(timestamp);
  loginRequest.set_sign(TEST_PROTOBUF_SIGN);
  loginRequest.set_platform(TEST_PROTOBUF_PLATFORM);

  char buffer[TEST_PROTOBUF_BUFFER_SIZE];
  Z_I32 len = loginRequest.ByteSize();
  ASSERT_GT(TEST_PROTOBUF_BUFFER_SIZE, len);
  EXPECT_TRUE(loginRequest.SerializeToArray(buffer, TEST_PROTOBUF_BUFFER_SIZE));
  
  kxPacketData::LoginRequest loginRequestCheck;
  EXPECT_TRUE(loginRequestCheck.ParseFromArray(buffer, len));
  EXPECT_EQ(len, loginRequestCheck.ByteSize());

  EXPECT_EQ(TEST_PROTOBUF_ID, loginRequestCheck.uid());
  EXPECT_EQ(TEST_PROTOBUF_ROOMID, loginRequestCheck.room_id());
  EXPECT_EQ(timestamp, loginRequestCheck.timestamp());
  EXPECT_STREQ(TEST_PROTOBUF_SIGN, loginRequestCheck.sign().c_str());
  EXPECT_EQ(TEST_PROTOBUF_PLATFORM, loginRequestCheck.platform());
}
#endif

#if TEST_all || TEST_json
TEST_F(utcase, json)
{

}
#endif

#if TEST_all || TEST_md5
typedef struct{
	Z_I32 id;
	Z_I32 timestamp;
  std::string sign;
} signData;

signData successData[] = {
  {1, 1350883739, std::string("d70683b02a95f90073bcfec8776a8aa7")}, 
  {123456789, 1350888465, std::string("a4cf34b4574e816319e30c22d080e5e9")}
};

TEST_F(utcase, md5)
{
  char signData[64];
  for ( int i = 0; i < sizeof(successData) / sizeof(signData); i++ )
  {
    Z_SNPRINTF(signData, sizeof(signData), "%u%u%s", successData[i].id, successData[i].timestamp, FLAGS_AgentKey.c_str());
    Z_U32 signLen = strnlen(signData, 64);
    EXPECT_TRUE(chatServer::md5Check((Z_U8*)signData, signLen, successData[i].sign));
  }
}
#endif

int main(int argc, char **argv) 
{
  google::InitGoogleLogging(argv[0]);

#ifdef __WIN32__
  google::SetLogDestination(INFO, "..\\..\\..\\logs\\chatserver_unit_INFO_");
  google::SetLogDestination(WARNING, "..\\..\\..\\logs\\chatserver_unit_WARNING_");
  google::SetLogDestination(ERROR, "..\\..\\..\\logs\\chatserver_unit_ERROR_");
  WSADATA wsaData;
  DWORD Ret;
  if ((Ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0)  
  {
    printf("WSAStartup failed with error %d\n", Ret);
    return -1;
  }
  CHECK_EQ(evthread_use_windows_threads(), 0);

#endif
#ifdef __LINUX__
  google::SetLogDestination(INFO, "/tmp/chatserver_unit_INFO_");
  google::SetLogDestination(WARNING, "/tmp/chatserver_unit_WARNING_");
  google::SetLogDestination(ERROR, "/tmp/chatserver_unit_ERROR_");
  CHECK_EQ(evthread_use_pthreads(), 0);
#endif

  ::testing::InitGoogleTest(&argc, argv);
  zsystem::objectFactory::init(objectScheme, propScheme);
  zsystem::objectFactory::isSchemeLoad();
  int r = RUN_ALL_TESTS();
#ifdef __WIN32__
  getchar();
  WSACleanup();  

#endif

  google::ShutdownGoogleLogging();

  return r;
}
